//
//  MujiPlayerObserver.m
//  ECCoreLibrary
//
//  Created by Matt Austin on 11/13/14.
//  Copyright (c) 2014 EnglishCentral, Inc. All rights reserved.
//

#import "MujiPlayerObserver.h"
#import <AVFoundation/AVFoundation.h>


static void *kMujiPlayerObserverContext = &kMujiPlayerObserverContext;


@interface MujiPlayerObserver ()

@property (readwrite, nonatomic, assign) NSKeyValueObservingOptions options;
@property (readwrite, nonatomic, assign) MujiPlayerObserverKVOs KVOs;
@property (readwrite, nonatomic, copy) NSDictionary *allKVOKeyPaths;

- (MujiPlayerObserverKVOs)KVOsForKeyPath:(NSString *)keyPath;

@end

@implementation MujiPlayerObserver

#pragma mark -
#pragma mark Synthesizers

@synthesize player         = _player;
@synthesize delegate       = _delegate;
@synthesize options        = _options;
@synthesize KVOs           = _KVOs;
@synthesize allKVOKeyPaths = _allKVOKeyPaths;

#pragma mark -
#pragma mark Setters

- (void)setPlayer:(AVPlayer *)player
{
   if (_player != player)
   {
      [self stopObserving];
      
      [self willChangeValueForKey:NSStringFromSelector(@selector(player))];
      _player = player;
      [self didChangeValueForKey:NSStringFromSelector(@selector(player))];
   }
}


#pragma mark -
#pragma mark Initializers

- (id)init
{
   self = [super init];
   if (self)
   {
      _options        = (NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew);
      _allKVOKeyPaths = [@{
                           @(MujiPlayerObserverKVOStatus) : NSStringFromSelector(@selector(status)),
                           @(MujiPlayerObserverKVORate)   : NSStringFromSelector(@selector(rate)),
                           @(MujiPlayerObserverKVOPlaybackBufferEmpty) : [NSString stringWithFormat:@"%@.%@",
                                                                        NSStringFromSelector(@selector(currentItem)),
                                                                        NSStringFromSelector(@selector(isPlaybackBufferEmpty))],
                           @(MujiPlayerObserverKVOPlaybackBufferFull) : [NSString stringWithFormat:@"%@.%@",
                                                                       NSStringFromSelector(@selector(currentItem)),
                                                                       NSStringFromSelector(@selector(isPlaybackBufferFull))],
                           @(MujiPlayerObserverKVOPlaybackLikelyToKeepUp) : [NSString stringWithFormat:@"%@.%@",
                                                                           NSStringFromSelector(@selector(currentItem)),
                                                                           NSStringFromSelector(@selector(isPlaybackLikelyToKeepUp))],
                           @(MujiPlayerObserverKVOLoadedTimeRanges) : [NSString stringWithFormat:@"%@.%@",
                                                                     NSStringFromSelector(@selector(currentItem)),
                                                                     NSStringFromSelector(@selector(loadedTimeRanges))],
                           @(MujiPlayerObserverKVOSeekableTimeRanges) : [NSString stringWithFormat:@"%@.%@",
                                                                       NSStringFromSelector(@selector(currentItem)),
                                                                       NSStringFromSelector(@selector(seekableTimeRanges))]
                           }
                         copy];
      _KVOs           = (MujiPlayerObserverKVOStatus | MujiPlayerObserverKVORate);
   }
   
   return self;
}


- (id)initWithPlayer:(AVPlayer *)player
{
   self = [self init];
   if (self)
   {
      _player = player;
   }
   
   return self;
}

- (id)initWithPlayer:(AVPlayer *)player
            delegate:(id<MujiPlayerObserverDelegate>)delegate
{
   self = [self initWithPlayer:player];
   if (self)
   {
      _delegate = delegate;
   }
   
   return self;
}


#pragma mark -
#pragma mark Public Methods

- (void)startObservingKeyPaths:(MujiPlayerObserverKVOs)KVOs
                   withOptions:(NSKeyValueObservingOptions)options
{
   [self stopObserving];
   
   if (self.player)
   {
      self.KVOs    = KVOs;
      self.options = options;
      NSDictionary *kvoStrings    = [self stringsForKeyPaths:self.KVOs];
      NSEnumerator *keyEnumerator = [kvoStrings keyEnumerator];
      NSNumber *key               = nil;
      while ((key = [keyEnumerator nextObject]))
      {
         @try
         {
            [self.player addObserver:self
                          forKeyPath:kvoStrings[key]
                             options:options
                             context:kMujiPlayerObserverContext];
         }
         @catch (NSException *__unused exception) {}
      }
      
   }
}

- (void)stopObserving
{
   if (self.player)
   {
      NSDictionary *kvoStrings    = [self stringsForKeyPaths:self.KVOs];
      NSEnumerator *keyEnumerator = [kvoStrings keyEnumerator];
      NSNumber *key               = nil;
      while ((key = [keyEnumerator nextObject]))
      {
         @try
         {
            [self.player removeObserver:self
                             forKeyPath:kvoStrings[key]
                                context:kMujiPlayerObserverContext];
         }
         @catch (NSException *__unused exception) {}
      }
   }
}

- (NSDictionary *)stringsForKeyPaths:(MujiPlayerObserverKVOs)KVOs
{
   NSMutableDictionary *mutableResults = [NSMutableDictionary dictionaryWithCapacity:self.allKVOKeyPaths.count];
   NSEnumerator *keyEnumerator         = [self.allKVOKeyPaths keyEnumerator];
   NSNumber *key                       = nil;
   while ((key = [keyEnumerator nextObject]))
   {
      if ([key unsignedIntegerValue] & KVOs)
      {
         mutableResults[key] = self.allKVOKeyPaths[key];
      }
   }
   
   return [mutableResults copy];
}


#pragma mark -
#pragma mark NSObject

- (void)dealloc
{
   [self stopObserving];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
   if (context == kMujiPlayerObserverContext)
   {
      if ([self.delegate respondsToSelector:@selector(playerObserver:keyPaths:change:)])
      {
         [self.delegate playerObserver:self
                              keyPaths:[self KVOsForKeyPath:keyPath]
                                change:change];
      }
      
      return;
   }
   
   [super observeValueForKeyPath:keyPath
                        ofObject:object
                          change:change
                         context:context];
}


#pragma mark -
#pragma mark Private Methods

- (MujiPlayerObserverKVOs)KVOsForKeyPath:(NSString *)keyPath
{
   if (!keyPath)
   {
      return NSNotFound;
   }
   
   MujiPlayerObserverKVOs KVOs = 0;
   NSDictionary *kvoStrings    = [self stringsForKeyPaths:self.KVOs];
   NSEnumerator *keyEnumerator = [kvoStrings keyEnumerator];
   NSNumber *key               = nil;
   while ((key = [keyEnumerator nextObject]))
   {
      if ([kvoStrings[key] isEqualToString:keyPath])
      {
         KVOs = ([key unsignedIntegerValue] | KVOs);
      }
   }
   
   return KVOs;
}


@end
